package no.gnet.edvd.web.controller;

import java.util.ArrayList;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;

import no.gnet.edvd.DataStore;
import no.gnet.edvd.engines.Engine;
import no.gnet.edvd.lightweight.CompactWinner;
import no.gnet.edvd.lightweight.EmailHolder;
import no.gnet.edvd.lightweight.NewEvent;
import no.gnet.edvd.types.ActivityType;
import no.gnet.edvd.types.Category;
import no.gnet.edvd.types.Event;
import no.gnet.edvd.types.Option;
import no.gnet.edvd.types.Player;
import no.gnet.edvd.util.Mailer;
import no.gnet.edvd.web.cache.MemCache;
import no.gnet.edvd.web.types.EdvdSessionUser;
import no.gnet.edvd.web.types.exception.IllegalActionException;
import no.gnet.edvd.web.types.exception.IncompleteNewEventException;
import no.gnet.edvd.web.types.exception.NoSessionUserException;
import no.gnet.edvd.web.types.exception.NotOwnerOfEventException;
import no.gnet.edvd.web.util.Constants;

import org.apache.commons.validator.routines.EmailValidator;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;


@Controller
public class EventController extends CommonController {

	
	@Autowired
	Engine engine;

	@Autowired
	DataStore datastore;	
	
	@Autowired
	Mailer mailer;
	
	@RequestMapping(value = "/api/event/{eventid}", method = RequestMethod.GET)
	public @ResponseBody
	EventDescriptor getEvent(HttpServletRequest request,
			@PathVariable String eventid) {
		int iid = Integer.parseInt(eventid);
		Event event = datastore.getBettableEvent(iid);
		EventDescriptor ed = getEDesc(eventid, event);
		EdvdSessionUser sessionUser = sessionUser(request);
		if (sessionUser != null)
			ed.setUserHasLiked(datastore.hasLiked(iid,
					sessionUser.getPlayer().id));
		ed.setTags(datastore.getTagsForEvent(iid));
		return ed;
	}
	
	@RequestMapping(value = "/api/ownevent/{eventid}", method = RequestMethod.GET)
	public @ResponseBody
	EventDescriptor getOwnEvent(HttpServletRequest request,
			@PathVariable String eventid) {
		int iid = Integer.parseInt(eventid);
		Event event = datastore.getEventForDecision(iid);
		EventDescriptor ed = getEDesc(eventid, event);
		EdvdSessionUser sessionUser = sessionUser(request);
		if (sessionUser != null)
			ed.setUserHasLiked(datastore.hasLiked(iid,
					sessionUser.getPlayer().id));
		ed.setTags(datastore.getTagsForEvent(iid));
		return ed;
	}
	
	@RequestMapping(value = "/setWinner", method = RequestMethod.POST, produces = "application/json", headers = { "Content-type=application/json" })
	public @ResponseBody
	CompactWinner setWinner(@RequestBody CompactWinner compactwinner, HttpServletRequest request) throws ServletException {
		assertWinnerOk(compactwinner, request);
		datastore.setWinnerForEvent(compactwinner.getEventid(), compactwinner.getOptionid());
		Player player = sessionUser(request).getPlayer();
		String text = player+ " set winner for event " + 
				compactwinner.getEventid() + ", winner is option with id " + compactwinner.getOptionid();
		datastore.logActivity(ActivityType.WINNER_SET, text, player);
		markSessionDirty(request);
		return compactwinner;
	}

	private void assertWinnerOk(CompactWinner compactwinner, HttpServletRequest request) {
		Event event = datastore.getEventForDecision(compactwinner.getEventid());
		// First of all, only owner of event can set winner
		EdvdSessionUser user = sessionUser(request);
		if(user.getPlayer().id!=event.owner.id){
			throw new NotOwnerOfEventException("Logged on user is not owner of event, cannot set winner");
		}
		// Next, option must be an option for the event...
		boolean hit=false;
		List<Option> options = event.getOptions();
		for (Option option : options) {
			if(option.id==compactwinner.getOptionid())
				hit=true;
		}
		if(!hit){
			throw new IllegalArgumentException("Optionid needs to match event");
		}
	}

	private EventDescriptor getEDesc(String eventid, Event event) {
		EventDescriptor ed = (EventDescriptor) MemCache
				.get(SINGLE_EVENT_CACHE_PREFIX + eventid);
		if (ed == null) {
			List<OptionWithOdds> optionsWithOdds = getOptionsWithOddsForEvent(
					event, datastore, engine);
			ed = new EventDescriptor(event, event.getDescription(),
					optionsWithOdds);
			MemCache.put(SINGLE_EVENT_CACHE_PREFIX + eventid, ed);
		}
		return ed;
	}	

	@RequestMapping(value = "/postEvent", method = RequestMethod.POST)
	public @ResponseBody
	int postEvent(@RequestBody NewEvent newEvent,
			HttpServletRequest request) {
		EdvdSessionUser sessionUser = sessionUser(request);
		Event event = sanityCheckInput(newEvent, sessionUser);
		
		Event storedevent = datastore.storeEvent(event);
		logger.debug("Stored event with id "+storedevent.id+",  full object: " + storedevent);
		int evid = storedevent.id;
		assertOKToActivate(evid, sessionUser);
		logger.debug("Activating " + evid);
		datastore.activateEvent(evid);
		if(newEvent.isPrivateEvent()){
			
			String eventpage=getEventPage(storedevent.id, request);
			sendMailToInvites(newEvent.getInvitedList(),storedevent,eventpage);
		}
		MemCache.markDirty(Constants.ALL_EVENT_DESCRIPTORS);
		return evid;
	}

	private void sendMailToInvites(List<EmailHolder> invitedList, Event event, String eventpage) {
		for(EmailHolder eh:invitedList){
		String message = String.format(
					"<h3>Hi!</h3><p> We are happy to say that you're invited to join a bet<p> It's called %s and betting deadline is "
					+ "%s <p> Go <a href=\"%s\">Link</a> to see the event page on eddevedde.com"
					+ "<p>Best regards, <i>EddeVedde Team</i>",event.getName(),event.getFormattedBetDeadline(),eventpage);
		
		mailer.sendHtmlMail("EddeVedde", eh.getEmail(),"Invitation to join a bet",message);
		logger.warn("Sent event invitation to "+eh.getEmail());
		}
	}

	private String getEventPage(int id, HttpServletRequest request) {
		String evpage = request.getRequestURL().toString().replace("/app/postEvent","/#/events/"+id);
		return evpage;
	}

	private void _checkDatesForNewEvent(Event event) {
		DateTime eventDecidedTime = event.eventDecidedTime;
		DateTime betDeadline = event.betDeadline;
		DateTime oneYearInTheFuture=new DateTime().plusYears(1);
		
		if( (eventDecidedTime==null) || (betDeadline==null) || eventDecidedTime.isBefore(betDeadline)){
			throw new IncompleteNewEventException("Dates are not ok. Remeber that betting deadline needs to be before time of event");
		}
		if( (eventDecidedTime.isBeforeNow()) || betDeadline.isBeforeNow()){
			throw new IncompleteNewEventException("Dates are not ok. Remeber that betting deadline or time of event can not be in the past");
		}
		
		Duration timeBetween = new Duration(eventDecidedTime, oneYearInTheFuture);
		logger.warn("Creating event, minutes away from one year: " + timeBetween.toPeriod().getMinutes());
	}

	private void _checkOptionsForNewEvent(Event event) {
		List<Option> options = event.getOptions();
		if(options==null || options.isEmpty()){
			throw new IncompleteNewEventException("An event needs to have at least 2 options");
		}
		
	}

	private void assertOKToActivate(int eventID, EdvdSessionUser user) {
		Event event = datastore.getBettableEvent(eventID);
		Player owner = event.getOwner();
		if (owner == null) {
			throw new IllegalActionException(
					"Ownerless event cannot be activated this way");
		}
		if ((owner.id) == user.getPlayer().id) {
			logger.debug("Ok, " + user.getPlayer().email
					+ " may activate event " + eventID);
		} else {
			throw new IllegalActionException(
					"Player requesting activation of event " + eventID
							+ " is not owner, (current player is " + user + ")");
		}
	}

	private Event sanityCheckInput(NewEvent newEvent, EdvdSessionUser sessionUser) {
		try {
			if(null==sessionUser){
				throw new NoSessionUserException("User not logged in");
			}			
			Assert.notNull(newEvent, "newEvent must be non-null");
			Assert.notNull(newEvent.getBetdeadline(), "newEvent.getBetdeadline() must be non-null");
			Assert.notNull(newEvent.getDescription(), "newEvent.getDescription must be non-null");
			Assert.notNull(newEvent.getEventtime(), "newEvent.getEventtime must be non-null");
			Assert.notNull(newEvent.getName(), "newEvent.getName must be non-null");
			Assert.notNull(newEvent.getOptions(), "newEvent.getOptions must be non-null");
			if(newEvent.isPrivateEvent()){
				Assert.notNull(newEvent.getInvitedList(), "newEvent.getOptions must be non-null");
				for(EmailHolder emailHolder : newEvent.getInvitedList()){
					Assert.isTrue(EmailValidator.getInstance().isValid(emailHolder.getEmail()),"Invited must have a valid email addresse");
				}
			}
		} catch (RuntimeException e) {
			logger.error(e);
			throw new IncompleteNewEventException(e.getMessage());
		}		
		logger.debug("About to store new event : " + newEvent);
		Event event = newEvent.toEvent();
		if(newEvent.getCategory()!=null){
			Category cat = Category.getCategoryByName(newEvent.getCategory());
			if(cat!=null){
				event.category=cat;
				logger.info("Managed to set category: "+cat.name);
			}
		}
		event.owner=sessionUser.getPlayer();
		_checkDatesForNewEvent(event);
		_checkOptionsForNewEvent(event);
		return event;
	}

	@RequestMapping(value = "/api/events/{from}/{count}", method = RequestMethod.GET)
	public @ResponseBody
	List<EventDescriptor> getEventDescriptors(@PathVariable String from,@PathVariable String count,HttpServletRequest request) {
		int ifrom = Integer.parseInt(from);
		int icount = Integer.parseInt(count);
		return refreshEventDescriptors(request, datastore, engine,ifrom,icount);
	}
	
	@RequestMapping(value = "/api/events/search/{query}", method = RequestMethod.GET)
	public @ResponseBody
	List<EventDescriptor> searchEventDescriptors(@PathVariable String query, HttpServletRequest request) {
		List<EventDescriptor> descs = refreshEventDescriptors(datastore, engine, request);
		List<EventDescriptor> querymatch = new ArrayList<EventDescriptor>();
		String qlc = query.toLowerCase();
		for(EventDescriptor ed : descs){
			String nlc = ed.getShortname().toLowerCase();
			String dlc = ed.getDescription().toLowerCase();
			if(nlc.contains(qlc)  || dlc.contains(qlc)){
				querymatch.add(ed);
			}
		}
		return querymatch;
	}	
	
	
}
